#include <gtest/gtest.h>
#include "UniquePtr.hpp"

#include <gtest/gtest.h>
#include "UniquePtr.hpp"

// -----------------------------------------------------------------------------
// 辅助结构：用于统计活跃对象数量，判断资源是否正确释放
// -----------------------------------------------------------------------------
struct Counter {
    static int alive;   ///< 当前仍然存活的对象数量（静态变量）

    Counter()  { ++alive; }  ///< 构造时递增
    ~Counter() { --alive; }  ///< 析构时递减
};

int Counter::alive = 0;  ///< 静态成员定义与初始化

// -----------------------------------------------------------------------------
// GoogleTest 测试用例
// -----------------------------------------------------------------------------

/**
 * @brief 测试 1：默认构造/空指针构造：应当不持有资源。
 */
TEST(UniquePtrTest, DefaultConstructedIsNull) {
    UniquePtr<int> ptr;             // 默认构造，内部指针为 nullptr
    EXPECT_FALSE(ptr);              // 转换为 bool 应为 false
    EXPECT_EQ(ptr.get(), nullptr);  // get() 返回裸指针，应为 nullptr
}

/**
 * @brief 测试 2：make_unique 创建对象后，指针应有效且可解引用。
 */
TEST(UniquePtrTest, MakeUniqueCreatesObject) {
    auto ptr = make_unique<int>(123); // 创建并包装新对象
    EXPECT_TRUE(ptr);                 // 指针非空
    EXPECT_EQ(*ptr, 123);             // 解引用读取到构造的值
}

/**
 * @brief 测试 3：移动构造应转移所有权，被移动对象置空。
 */
TEST(UniquePtrTest, MoveTransfersOwnership) {
    auto ptr = make_unique<int>(42);  // 原始拥有者
    UniquePtr<int> moved = std::move(ptr); // 移动构造

    EXPECT_FALSE(ptr);                // 被移动后，原对象为空
    EXPECT_TRUE(moved);               // 新对象接管资源
    EXPECT_EQ(*moved, 42);            // 数据保持
}

/**
 * @brief 测试 4：reset() 会释放旧资源并接管新资源。
 */
TEST(UniquePtrTest, ResetReleasesOldResource) {
    EXPECT_EQ(Counter::alive, 0);     // 初始化时没有活动对象

    {
        auto ptr = make_unique<Counter>();
        EXPECT_EQ(Counter::alive, 1); // 构造函数被调用一次

        // 接管一个新的 Counter，同时旧对象应被析构
        ptr.reset(new Counter());
        EXPECT_EQ(Counter::alive, 1); // 依旧为 1：先析构旧对象，再构造新对象

        // 释放当前资源
        ptr.reset();
        EXPECT_EQ(Counter::alive, 0);
    }

    // 离开作用域后，无额外资源存在
    EXPECT_EQ(Counter::alive, 0);
}

/**
 * @brief 测试 5：release() 放弃所有权但不删除对象，返回裸指针。
 */
TEST(UniquePtrTest, ReleaseReturnsRawPointer) {
    auto ptr = make_unique<int>(99);
    int* raw = ptr.release();        // 放弃所有权，raw 接管裸指针

    EXPECT_EQ(ptr.get(), nullptr);   // UniquePtr 内部指针为空
    EXPECT_FALSE(ptr);               // bool 转换为 false
    EXPECT_NE(raw, nullptr);         // raw 获得原始地址
    EXPECT_EQ(*raw, 99);             // 裸指针仍可使用

    delete raw;                      // 手动释放，避免内存泄漏
}

/**
 * @brief 测试 6：自定义 lambda 删除器：验证 reset 与析构时是否执行。
 */
TEST(UniquePtrTest, CustomLambdaDeleterIsCalled) {
    int call_count = 0;

    auto deleter = [&call_count](int* p) {
        delete p;                    // 同样释放内存
        ++call_count;                // 记录删除器被调用次数
    };

    {
        UniquePtr<int, decltype(deleter)> ptr(new int(7), deleter);
        EXPECT_EQ(call_count, 0);    // 构造时不会触发删除器

        ptr.reset();                 // 主动释放
        EXPECT_EQ(call_count, 1);    // 删除器被调用一次
    }

    // 由于指针已 reset，离开作用域不会再次调用删除器
    EXPECT_EQ(call_count, 1);
}